package com.example.demo.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.demo.common.ScheduledTask;
import com.example.demo.entity.*;
import com.example.demo.entity.vo.Roommate;
import com.example.demo.exception.CmsException;
import com.example.demo.exception.ErrorCode;
import com.example.demo.mapper.*;
import com.example.demo.service.OrderInfoService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.demo.service.UserService;
import com.example.demo.utils.RedisUtil;
import com.sun.org.apache.xpath.internal.operations.Or;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import sun.font.ScriptRun;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.*;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author chen huilong
 * @since 2022-10-04
 */
@Service
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {

    @Resource
    private RedisUtil redisUtil;

    @Resource
    private UserMapper userMapper;

    @Autowired
    private UserService userService;

    @Resource
    private BuildingInfoMapper buildingInfoMapper;

    @Resource
    private GroupInfoMapper groupInfoMapper;

    @Resource
    private RoomInfoMapper roomInfoMapper;

    @Resource
    private OrderInfoMapper orderInfoMapper;

    /**
     * 提交订单
     *
     * @param access_token
     * @param request
     * @param buildingId
     * @return
     */
    @Override
    public Map<String, Object> submitOrder(String access_token, HttpServletRequest request, String buildingId) {

        // 这个地方改成从数据库取用户的最新信息，可能存在组员先抢到宿舍的情况，session中的用户信息没法及时更新

        HttpSession session = request.getSession();
        User user = (User) session.getAttribute(access_token);

        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("user_number", user.getUserNumber());
        user = userMapper.selectOne(wrapper);

        // 为了测试接口性能，这里不进行判断，只要有请求进来都会给分配空宿舍
        // 已有宿舍
        //if (user.getRoomId() != null && !"".equals(user.getRoomId())) {
        //    throw new CmsException(ErrorCode.HAVE_ROOM);
        //}

        // 当前日期
        Date date = new Date();

        // 记录队伍中的人数
        int peopleCount = 0;

        // 查宿舍楼的剩余床位信息
        BuildingInfo buildingInfo = buildingInfoMapper.selectById(buildingId);
        if (buildingInfo == null) {
            throw new CmsException(ErrorCode.WRONG_BUILDING_ID);
        }

        // 指定性别在当前宿舍楼中剩余可分配床位数量
        int count = bedCountGender(user.getGender(), buildingInfo);

        Long groupId = user.getGroupId();
        LinkedList<Long> studentNums = new LinkedList<>();

        if (groupId == null) {
            // 学生没组队，单人抢宿舍
            peopleCount = 1;
            // 房间数量 < 队伍人数
            if (count < peopleCount) {
                throw new CmsException(ErrorCode.QUANTITY_NOT_ENOUGH);
            }
            studentNums.add(user.getUserNumber());
        } else {
            // 查组队信息
            GroupInfo groupInfo = groupInfoMapper.selectById(groupId);
            peopleCount = groupInfo.getCount();
            if (count < peopleCount) {
                throw new CmsException(ErrorCode.QUANTITY_NOT_ENOUGH);
            }
            // 这里有在考虑修改数据表结构，把队伍中的所有成员学号拼接起来存在一列中
            if (groupInfo.getStudentId1() != null) {
                studentNums.add(groupInfo.getStudentId1());
            }
            if (groupInfo.getStudentId2() != null) {
                studentNums.add(groupInfo.getStudentId2());
            }
            if (groupInfo.getStudentId3() != null) {
                studentNums.add(groupInfo.getStudentId3());
            }
            if (groupInfo.getStudentId4() != null) {
                studentNums.add(groupInfo.getStudentId4());
            }
        }
        // 分配宿舍，记录订单，修改信息，返回宿舍号
        String roomId = handleOrder(user, buildingInfo, peopleCount, date, studentNums);

        // 修改session
        user.setRoomId(roomId);
        session.setAttribute(access_token, user);

        // 根据宿舍号查询同住人信息并返回
        List<Roommate> users = userMapper.selectListByRoomId(roomId);
        HashMap<String, Object> stringObjectHashMap = new HashMap<>();
        stringObjectHashMap.put("roomId", roomId);
        stringObjectHashMap.put("roommates", users);
        return stringObjectHashMap;
    }

    /**
     * 指定性别在当前宿舍楼中还有多少个可分配床位
     *
     * @param gender
     * @param buildingInfo
     * @return
     */
    private int bedCountGender(String gender, BuildingInfo buildingInfo) {
        if ("0".equals(gender)) {
            return buildingInfo.getBedCountMan();
        } else {
            return buildingInfo.getBedCountWoman();
        }
    }


    private String handleOrder(User user, BuildingInfo buildingInfo, int peopleCount,
                               Date date, List<Long> studentNums) {
        // 记录订单信息
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setUserId(user.getUserId());
        orderInfo.setBuildingName(buildingInfo.getBuildingName());
        orderInfo.setUserCount(peopleCount);
        orderInfo.setSubmitTime(date);

        // 条件查询，找出一个可用房间
        QueryWrapper<RoomInfo> roomInfoQueryWrapper = new QueryWrapper<>();
        roomInfoQueryWrapper.eq("user_gender", user.getGender());
        roomInfoQueryWrapper.eq("building_id", buildingInfo.getBuildingId());
        // 大于等于
        roomInfoQueryWrapper.ge("bed_remainder", peopleCount);
        // 限制只查第一条满足条件的记录
        roomInfoQueryWrapper.last("limit 1");

        RoomInfo roomInfo = roomInfoMapper.selectOne(roomInfoQueryWrapper);
        if (roomInfo == null) {
            // 没找到，设置订单状态并保存
            orderInfo.setOrderStatus("0");
            orderInfoMapper.insert(orderInfo);
            throw new CmsException(ErrorCode.QUANTITY_NOT_ENOUGH);
        } else {
            // 找到可分配宿舍，修改信息（房间、宿舍楼、学生信息三部分）
            modifyRoomAndBuildingAndStuInfo(studentNums, roomInfo, user, buildingInfo);
            // 设置订单状态并保存
            orderInfo.setOrderStatus("1");
            orderInfoMapper.insert(orderInfo);
        }
        return roomInfo.getRoomId();
    }


    /**
     * 抢宿舍成功后，修改宿舍、宿舍楼、学生信息
     * 要保证原子性，即必须三个信息全部修改成功，否则必须回滚
     *
     * @param roomInfo
     * @param user
     * @param buildingInfo
     */
    @Transactional(rollbackFor = Exception.class)
    public void modifyRoomAndBuildingAndStuInfo(List<Long> studentNums,
                                                RoomInfo roomInfo,
                                                User user,
                                                BuildingInfo buildingInfo) {
        int sz = studentNums.size();
        // 修改房间信息
        roomInfo.setBedRemainder(roomInfo.getBedRemainder() - sz);
        roomInfoMapper.updateById(roomInfo);
        // 修改宿舍楼信息
        if ("0".equals(roomInfo.getUserGender())) {
            buildingInfo.setBedRemainderMan(buildingInfo.getBedRemainderMan() - sz);
        } else {
            buildingInfo.setBedRemainderWoman(buildingInfo.getBedRemainderWoman() - sz);
        }
        buildingInfoMapper.updateById(buildingInfo);
        // 修改学生信息
        List<User> users = userMapper.selectListByNums(studentNums);
        for (User u : users) {
            u.setRoomId(roomInfo.getRoomId());
        }
        // 批量更新
        userService.saveOrUpdateBatch(users);
    }

    @Override
    public void getAllOrder(String token) {

    }

    @Override
    public void getOrderResult(String token, String orderId) {

    }

    @Override
    public Map<String, Object> submitOrderWithRedis(String access_token, HttpServletRequest request, String buildingId) {
        // 这个地方改成从数据库取用户的最新信息，可能存在组员先抢到宿舍的情况，session中的用户信息没法及时更新

        HttpSession session = request.getSession();
        User user = (User) session.getAttribute(access_token);

        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("user_number", user.getUserNumber());
        user = userMapper.selectOne(wrapper);

        // 为了测试接口性能，这里不进行判断，只要有请求进来都会给分配空宿舍
        // 已有宿舍
        //if (user.getRoomId() != null && !"".equals(user.getRoomId())) {
        //    throw new CmsException(ErrorCode.HAVE_ROOM);
        //}

        // 当前日期
        Date date = new Date();

        // 记录队伍中的人数
        int peopleCount = 0;

        // 查宿舍楼的剩余床位信息
        BuildingInfo buildingInfo = (BuildingInfo) redisUtil.get(buildingId);
        if (buildingInfo == null) {
            throw new CmsException(ErrorCode.WRONG_BUILDING_ID);
        }

        // 指定性别在当前宿舍楼中剩余可分配床位数量
        int count = bedCountGender(user.getGender(), buildingInfo);

        Long groupId = user.getGroupId();
        LinkedList<Long> studentNums = new LinkedList<>();

        if (groupId == null) {
            // 学生没组队，单人抢宿舍
            peopleCount = 1;
            // 房间数量 < 队伍人数
            if (count < peopleCount) {
                throw new CmsException(ErrorCode.QUANTITY_NOT_ENOUGH);
            }
            studentNums.add(user.getUserNumber());
        } else {
            // 查组队信息
            GroupInfo groupInfo = groupInfoMapper.selectById(groupId);
            peopleCount = groupInfo.getCount();
            if (count < peopleCount) {
                throw new CmsException(ErrorCode.QUANTITY_NOT_ENOUGH);
            }
            // 这里有在考虑修改数据表结构，把队伍中的所有成员学号拼接起来存在一列中
            if (groupInfo.getStudentId1() != null) {
                studentNums.add(groupInfo.getStudentId1());
            }
            if (groupInfo.getStudentId2() != null) {
                studentNums.add(groupInfo.getStudentId2());
            }
            if (groupInfo.getStudentId3() != null) {
                studentNums.add(groupInfo.getStudentId3());
            }
            if (groupInfo.getStudentId4() != null) {
                studentNums.add(groupInfo.getStudentId4());
            }
        }
        // 分配宿舍，记录订单，修改信息，返回宿舍号
        String roomId = handleOrderWithRedis(user, buildingInfo, peopleCount, date, studentNums);

        // 修改session
        user.setRoomId(roomId);
        session.setAttribute(access_token, user);

        // 根据宿舍号查询同住人信息并返回
        List<Roommate> users = userMapper.selectListByRoomId(roomId);
        HashMap<String, Object> stringObjectHashMap = new HashMap<>();
        stringObjectHashMap.put("roomId", roomId);
        stringObjectHashMap.put("roommates", users);
        return stringObjectHashMap;
    }

    private static String ROOM = "ROOM";

    private String handleOrderWithRedis(User user, BuildingInfo buildingInfo, int peopleCount,
                                        Date date, List<Long> studentNums) {
        // 记录订单信息
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setUserId(user.getUserId());
        orderInfo.setBuildingName(buildingInfo.getBuildingName());
        orderInfo.setUserCount(peopleCount);
        orderInfo.setSubmitTime(date);

        // 条件查询，找出一个可用房间
        List<RoomInfo> roomInfos = (List<RoomInfo>) redisUtil.get(ROOM + buildingInfo.getBuildingId());
        RoomInfo roomInfo = null;
        for (RoomInfo r : roomInfos) {
            if (user.getGender().equals(r.getUserGender()) && studentNums.size() <= r.getBedRemainder()) {
                roomInfo = r;
                break;
            }
        }


        if (roomInfo == null) {
            // 没找到，设置订单状态并保存
            orderInfo.setOrderStatus("0");
            orderInfoMapper.insert(orderInfo);
            throw new CmsException(ErrorCode.QUANTITY_NOT_ENOUGH);
        } else {
            // 找到可分配宿舍，修改信息（房间、宿舍楼、学生信息三部分）
            modifyRoomAndBuildingAndStuInfoWithRedis(studentNums, roomInfo, roomInfos, user, buildingInfo);
            // 设置订单状态并保存
            orderInfo.setOrderStatus("1");
            orderInfoMapper.insert(orderInfo);
        }
        return roomInfo.getRoomId();
    }


    /**
     * 抢宿舍成功后，修改宿舍、宿舍楼、学生信息
     * 要保证原子性，即必须三个信息全部修改成功，否则必须回滚
     *
     * @param roomInfo
     * @param user
     * @param buildingInfo
     */
    @Transactional(rollbackFor = Exception.class)
    public void modifyRoomAndBuildingAndStuInfoWithRedis(List<Long> studentNums,
                                                         RoomInfo roomInfo,
                                                         List<RoomInfo> roomInfos,
                                                         User user,
                                                         BuildingInfo buildingInfo) {
        int sz = studentNums.size();
        // 修改房间信息
        roomInfo.setBedRemainder(roomInfo.getBedRemainder() - sz);
        redisUtil.set(ROOM + buildingInfo.getBuildingId(), roomInfos);
        // 修改宿舍楼信息
        if ("0".equals(roomInfo.getUserGender())) {
            buildingInfo.setBedRemainderMan(buildingInfo.getBedRemainderMan() - sz);
        } else {
            buildingInfo.setBedRemainderWoman(buildingInfo.getBedRemainderWoman() - sz);
        }
        redisUtil.set(buildingInfo.getBuildingId() + "", buildingInfo);
        // 修改学生信息
        List<User> users = userMapper.selectListByNums(studentNums);
        for (User u : users) {
            u.setRoomId(roomInfo.getRoomId());
        }
        // 批量更新
        userService.saveOrUpdateBatch(users);
    }
}
